//
//  YTWebScrollGestureControl.swift
//  UIKit物理效果
//
//  Created by 柯南 on 2022/9/19.
//


//MARK:- TableView 与 webView 联动滚动
import UIKit
import WebKit

@objc public protocol YTWebScrollGestureControlDelegate: AnyObject {
    /// 正在滚动
    @objc optional func webScrollGestureControlDidScroll(_ control: YTWebScrollGestureControl)
}

// YTWebScrollGestureControl 通过手势进行滚动 webveiw 与 scrollview
@objcMembers
public class YTWebScrollGestureControl: NSObject {
    //MARK:- Public
    // 底部弹性
    public var bottomBounces: Bool = true
    // 滚动进度
    public var indicatorProgress: CGFloat = 0
    // contentOffset
    public var contentOffset: CGPoint = .zero
    // 代理
    public weak var delegate: YTWebScrollGestureControlDelegate?
    
    //MARK:- Private
    // referenceView 添加在最底层的 view 上
    private var referenceView: YTReferenceView
    private var tableView: UITableView
    private var webView: WKWebView
    
    private lazy var animator = UIDynamicAnimator(referenceView: self.referenceView)
    private lazy var item = YTItemBehavior()
    
    //MARK:- data
    // 拖动旧的Y值
    private var oldPanGetstureY: CGFloat = 0
    // 当前ItemBehavior中心Y值
    private var itemCenterY: CGFloat = 0
    // 旧的ItemBehavior中心Y值
    private var oldItemCenterY: CGFloat = 0
    
    // 旧的WebView偏移Y值
    private var webViewOldOffsetY: CGFloat = 0
    // webView offsetY 起点
    private var webViewBeganOffsetY: CGFloat = 0
    // webView offsetY 终点
    private var webViewMaxOffsetY: CGFloat  {
        let maxOffsetY = webView.scrollView.contentSize.height - webView.bounds.height
        return maxOffsetY <= 0 ? 0 : maxOffsetY
    }
    
    // 旧的tabview偏移Y值
    private var tableViewOldOffsetY: CGFloat = 0
    // tableView offsetY 起点
    private var tableViewBeganOffsetY: CGFloat { -tableView.contentInset.top }
    // tableView offsetY 终点
    private var tableViewMaxOffsetY: CGFloat {
        let maxOffsetY = tableView.contentSize.height - tableView.bounds.height + (tableView.contentInset.bottom)
        return maxOffsetY <= 0 ? 0 : maxOffsetY
    }
    
    /// 初始化
    /// - Parameters:
    ///   - referenceView: 最底层的 view
    ///   - tableView: tableView
    @objc public init(referenceView: YTReferenceView, tableView: UITableView, webView: WKWebView) {
        self.referenceView = referenceView
        self.tableView = tableView
        self.webView = webView
        
        super.init()
        setup()
    }
    
    private func setup() {
        self.tableView.bounces = false
        self.tableView.isScrollEnabled = false
        self.tableView.showsVerticalScrollIndicator = false
        self.tableView.showsHorizontalScrollIndicator = false
        self.tableView.addObserver(self, forKeyPath: "contentOffset", options: .new, context: nil)
        
        self.webView.scrollView.bounces = false
        self.webView.scrollView.isScrollEnabled = false
        self.webView.scrollView.showsVerticalScrollIndicator = false
        self.webView.scrollView.showsHorizontalScrollIndicator = false
        self.webView.scrollView.addObserver(self, forKeyPath: "contentSize", options: .new, context: nil)
        self.webView.scrollView.addObserver(self, forKeyPath: "contentOffset", options: .new, context: nil)
        self.webView.addObserver(self, forKeyPath: "estimatedProgress", options: .new, context: nil)
        
        // 长按收拾，主要控制滚动与惯性
        let panGesture = UIPanGestureRecognizer(target: self, action: #selector(panGesture(_:)))
        self.referenceView.addGestureRecognizer(panGesture)
        
        // 点击手势主要负责控制cell的点击
        let tapGesture = UITapGestureRecognizer(target: self, action: #selector(tapGesture(_:)))
        self.tableView.addGestureRecognizer(tapGesture)
        
        // 负责惯性滚动时的点击停止滚动
        referenceView.touchBeganCallBack = { [weak self] in
            self?.behaviorEndAnimation()
        }
    }
    
    public override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
        guard let scrollView = object as? UIScrollView else { return }
        
        if scrollView == self.webView.scrollView {
            if keyPath == "contentSize" {
                let size = change?[.newKey] as? CGSize
                if webView.scrollView.contentSize.height != size?.height {
                    tableView.contentOffset.y = 0
                    webView.scrollView.contentOffset.y = 0
                }
            }else if keyPath == "contentOffset" {
                if webView.scrollView.contentOffset.y < webView.scrollView.contentSize.height - webView.bounds.height {
                    tableView.contentOffset.y = 0
                }
                
                webScrollGestureControlDidScroll()
            }else if keyPath == "estimatedProgress" {
                if webView.estimatedProgress >= 1 {
                    tableView.contentOffset.y = 0
                    webView.scrollView.contentOffset.y = 0
                }
            }
        }else {
            if keyPath == "contentOffset" {
                webScrollGestureControlDidScroll()
            }
        }
    }
    
    deinit {
        self.webView.removeObserver(self, forKeyPath: "estimatedProgress")
        self.webView.scrollView.removeObserver(self, forKeyPath: "contentSize")
        self.webView.scrollView.removeObserver(self, forKeyPath: "contentOffset")

        self.tableView.removeObserver(self, forKeyPath: "contentOffset")
    }
}

//MARK:- Action
private extension YTWebScrollGestureControl {
    @objc func tapGesture(_ gesture: UITapGestureRecognizer) {
        let point = gesture.location(in: tableView)
        if let indexPath = tableView.indexPathForRow(at: point) {
            tableView.delegate?.tableView?(tableView, didSelectRowAt: indexPath)
        }
    }
    
    @objc func panGesture(_ gesture: UIPanGestureRecognizer) {
        switch gesture.state {
        case .began:
            gestureBegan(gesture)
            break
        case .ended:
            gestureEnded(gesture)
            break
        case .changed:
            gestureChanged(gesture)
            break
        case .possible:
            notBehaviorAnimation()
            break
        case .cancelled:
            notBehaviorAnimation()
            break
        case .failed:
            notBehaviorAnimation()
            break
        @unknown default:
            notBehaviorAnimation()
            break
        }
    }
    
    /// 手势开始
    func gestureBegan(_ gesture: UIPanGestureRecognizer) {
        animator.removeAllBehaviors()
        itemCenterY = item.center.y
        
        oldPanGetstureY = gesture.location(in: self.referenceView).y
        tableViewOldOffsetY = tableView.contentOffset.y
        webViewOldOffsetY = webView.scrollView.contentOffset.y
    }
    
    /// 手势结束
    func gestureEnded(_ gesture: UIPanGestureRecognizer) {
        // 计算惯性滚动
        let pointY = gesture.velocity(in: self.referenceView).y
        if abs(pointY) >= 180 { // 设置滑动速度大于180才执行惯性动画

            let behavior = UIDynamicItemBehavior(items: [item])
            behavior.resistance = 2.0
            behavior.addLinearVelocity(.init(x: 0, y: pointY), for: item)

            behavior.action = { [weak self] in // 根据惯性进行滚动
                guard let weakSelf = self else { return }
                let dValue = weakSelf.item.center.y - weakSelf.oldItemCenterY
                
                // 即将结束惯性
                if abs(dValue) < 0.02 {
                    weakSelf.behaviorWillEndAnimation()
                }else {
                    weakSelf.tableViewUserInteractionEnabledState(false)
                }
                
                // 确定滚动方向
                if weakSelf.item.center.y < weakSelf.oldItemCenterY { // tableView offsetY 增加
                    if weakSelf.webViewOldOffsetY >= weakSelf.webViewMaxOffsetY { // webView 滚到底部，tabview 滚动
                        weakSelf.setScrollOffsetY(dValue: dValue, isWebView: false)
                        
                        // tableView 滚动到底部时，结束惯性
                        if weakSelf.tableViewOldOffsetY >= weakSelf.tableViewMaxOffsetY {
                            weakSelf.behaviorEndAnimation()
                        }
                    }else { // webView 没滚动到底部，webView 滚动
                        weakSelf.setScrollOffsetY(dValue: dValue, isWebView: true)
                    }

                }else { // tableView offsetY 减少或不变
                    if weakSelf.tableViewOldOffsetY <= weakSelf.tableViewBeganOffsetY { // tableView 滚到顶部，webView滚动
                        weakSelf.setScrollOffsetY(dValue: dValue, isWebView: true)
                        
                        // webView 滚动到顶部时，结束惯性
                        if weakSelf.webViewOldOffsetY <= weakSelf.webViewBeganOffsetY {
                            weakSelf.behaviorEndAnimation()
                        }
                    }else { // tableView 没滚到顶部， tableView 滚动
                        weakSelf.setScrollOffsetY(dValue: dValue, isWebView: false)
                    }
                }

                weakSelf.oldItemCenterY = weakSelf.item.center.y
            }

            animator.addBehavior(behavior)
        }else { // 不需要惯性
            notBehaviorAnimation()
        }
    }
    
    /// 手势改变
    func gestureChanged(_ gesture: UIPanGestureRecognizer) {
        // 设置滚动时不响应tabview
        tableViewUserInteractionEnabledState(false)
        
        let currentY = gesture.location(in: self.referenceView).y
        let dValue = currentY - oldPanGetstureY
        
        // 确定滚动方向
        if currentY < oldPanGetstureY { // tableView offsetY 增加
            if webViewOldOffsetY >= webViewMaxOffsetY { // webView 滚到底部，tabview 滚动
                setScrollOffsetY(dValue: dValue, isWebView: false)
                
            }else { // webView 没滚动到底部，webView 滚动
                setScrollOffsetY(dValue: dValue, isWebView: true)
            }
        }else { // tableView offsetY 减少或不变
            if tableViewOldOffsetY <= tableViewBeganOffsetY { // tableView 滚到顶部，webView滚动
                setScrollOffsetY(dValue: dValue, isWebView: true)
            }else { // tableView 没滚到顶部， tableView 滚动
                setScrollOffsetY(dValue: dValue, isWebView: false)
            }
        }
        
        // 重置旧值
        oldPanGetstureY = currentY
    }
    
//     设置滚动 OffsetY
    func setScrollOffsetY(dValue: CGFloat, isWebView: Bool) {
        let oldOffsetY = isWebView ? webViewOldOffsetY : tableViewOldOffsetY
        let beganOffsetY = isWebView ? webViewBeganOffsetY : tableViewBeganOffsetY
        let maxOffsetY = isWebView ? webViewMaxOffsetY : tableViewMaxOffsetY

        // 新值
        var newOffsetY = oldOffsetY - dValue
        newOffsetY = newOffsetY >= beganOffsetY ? newOffsetY : beganOffsetY

        if isWebView {
            // 限制最大值
            newOffsetY = newOffsetY >= maxOffsetY ? maxOffsetY : newOffsetY
            webView.scrollView.contentOffset.y = newOffsetY
            webViewOldOffsetY = newOffsetY
        }else {
            if newOffsetY >= maxOffsetY { // 弹起最大150
                if bottomBounces {
                    // 超出最大值进度
                    let progress = (((maxOffsetY + 150) - tableViewOldOffsetY) / 150) * 0.3

                    // 偏移量
                    let value = (newOffsetY - tableViewOldOffsetY) * progress
                    newOffsetY = tableViewOldOffsetY + value
                }else {
                    newOffsetY = newOffsetY >= maxOffsetY ? maxOffsetY : newOffsetY
                }
            }

            tableView.contentOffset.y = newOffsetY
            tableViewOldOffsetY = newOffsetY
        }
    }
    
    /// 关闭惯性动画
    func notBehaviorAnimation() {
        tableViewUserInteractionEnabledState(true)
        
        if tableViewOldOffsetY > tableViewMaxOffsetY {
            tableView.setContentOffset(.init(x: 0, y: tableViewMaxOffsetY), animated: true)
            tableViewOldOffsetY = tableViewMaxOffsetY
        }
    }
    
    /// 惯性动画即将结束
    func behaviorWillEndAnimation() {
        tableViewUserInteractionEnabledState(true)
    }
    
    /// 结束惯性动画
    func behaviorEndAnimation() {
        animator.removeAllBehaviors()
        notBehaviorAnimation()
    }
    
    /// 设置 tableView 是否允许交互
    func tableViewUserInteractionEnabledState(_ state: Bool) {
        tableView.isUserInteractionEnabled = state
    }
    
    // 正在滚动
    func webScrollGestureControlDidScroll() {
        // 滚动进度
        let maxProgress = (webView.scrollView.contentSize.height - webView.scrollView.bounds.height) +  (tableView.contentSize.height - tableView.bounds.height + tableView.contentInset.top + tableView.contentInset.bottom)

        let currentProgress = webView.scrollView.contentOffset.y + tableView.contentOffset.y
        indicatorProgress = currentProgress/maxProgress
        
        // contentOffset
        contentOffset = .init(x: 0, y: currentProgress)
        
        // 正在滚动
        delegate?.webScrollGestureControlDidScroll?(self)
    }
}

class YTItemBehavior: NSObject, UIDynamicItem {
    var center: CGPoint
    
    var bounds: CGRect
    
    var transform: CGAffineTransform
    
    override init() {
        bounds = .init(x: 10, y: 0, width: 10, height: 10)
        center = .zero
        transform = .identity
    }
}

@objcMembers
public class YTReferenceView: UIView {
    var touchBeganCallBack: (() -> Void)?
    
    public override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        touchBeganCallBack?()
    }
}

